/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.inspur.edp.lcm.metadata.common;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.inspur.edp.lcm.metadata.api.IMdExtRuleContent;
import com.inspur.edp.lcm.metadata.api.IMetadataContent;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.common.configuration.MdExtRuleSerializerHelper;
import com.inspur.edp.lcm.metadata.common.configuration.MetadataSerializerHelper;
import com.inspur.edp.lcm.metadata.spi.MdExtendRuleSerializer;
import com.inspur.edp.lcm.metadata.spi.MetadataContentSerializer;
import io.iec.edp.caf.commons.layeringcache.serializer.RedisDataSerializer;
import java.io.IOException;

public class MetadataSerializer implements RedisDataSerializer {
    private ObjectMapper mapper = Utils.getMapper();

    @Override
    public <T> T deserialize(String fileString, Class<T> clazz) {
        if (fileString == null || fileString.isEmpty()) {
            return null;
        }
        try {
            IMetadataContent metadataContent;
            JsonNode metadataObj = mapper.readTree(fileString);
            GspMetadata metadata = buildMetadataBaseInfo(metadataObj);

            //IMetdataContent需要调用各元数据反序列化
            MetadataContentSerializer manager = MetadataSerializerHelper.getInstance().getManager(metadata.getHeader().getType());
            if (manager == null) {
                throw new RuntimeException("未能正常获取元数据序列化器，请检查配置,元数据类型为：" + metadata.getHeader().getType());
            } else {
                JsonNode content = metadataObj.findValue(MetadataPeopertyUtils.content);
                String contentStr = handleJsonString(content.toString());
                JsonNode handledContent = mapper.readTree(contentStr);
                metadataContent = manager.deSerialize(handledContent);
            }
            metadata.setContent(metadataContent);
            JsonNode extRule = metadataObj.findValue(MetadataPeopertyUtils.extendRule);
            if (extRule != null) {
                MdExtendRuleSerializer mdExtRulemanager = MdExtRuleSerializerHelper.getInstance().getManager(metadata.getHeader().getType());
                if (mdExtRulemanager != null) {
                    JsonNode handledContent = mapper.readTree(extRule.toString());
                    IMdExtRuleContent extRuleContent = mdExtRulemanager.deSerialize(handledContent);
                    metadata.setExtendRule(extRuleContent);
                }
            }

            return (T) metadata;
        } catch (RuntimeException | IOException e) {
            e.printStackTrace();
            throw new RuntimeException("反序列化元数据失败", e);
        }
    }

    public GspMetadata buildMetadataBaseInfo(JsonNode metadataObj) {
        GspMetadata metadata;
        try {
            String headerStr = metadataObj.findValue(MetadataPeopertyUtils.header).toString();
            JsonNode refNode = metadataObj.findValue(MetadataPeopertyUtils.refs);
            String constraintsStr;
            if (refNode == null) {
                constraintsStr = "[]";
            } else {
                constraintsStr = refNode.toString();
            }

            JsonNode extendPropertyNode = metadataObj.findValue(MetadataPeopertyUtils.extendProperty);
            JsonNode extendedNode = metadataObj.findValue(MetadataPeopertyUtils.extended);
            JsonNode properties = metadataObj.findValue(MetadataPeopertyUtils.properties);
            JsonNode version = metadataObj.findValue(MetadataPeopertyUtils.version);
            JsonNode previousVersion = metadataObj.findValue(MetadataPeopertyUtils.previousVersion);
            JsonNode relativePath = metadataObj.findValue(MetadataPeopertyUtils.relativePath);
            String extendPropertyStr = (extendPropertyNode == null) ? "\"\"" : extendPropertyNode.toString();
            String metadataStr = String.format("{\"%s\":%s,\"%s\":%s,\"%s\":null,\"%s\":%s,\"%s\":%s,\"%s\":%s,\"%s\":%s,\"%s\":%s,\"%s\":%s,\"%s\":null}",
                MetadataPeopertyUtils.header, headerStr, MetadataPeopertyUtils.refs, constraintsStr, MetadataPeopertyUtils.content, MetadataPeopertyUtils.extendProperty,
                extendPropertyStr, MetadataPeopertyUtils.extended, extendedNode, MetadataPeopertyUtils.properties, properties, MetadataPeopertyUtils.version, version,
                MetadataPeopertyUtils.previousVersion, previousVersion, MetadataPeopertyUtils.relativePath, relativePath, MetadataPeopertyUtils.extendRule);
            metadata = this.mapper.readValue(metadataStr, GspMetadata.class);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

        return metadata;
    }

    public static ObjectMapper getMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);
        mapper.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true);
        mapper.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true);
        mapper.configure(JsonParser.Feature.ALLOW_MISSING_VALUES, true);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);//大小写脱敏 默认为false  需要改为tru
        return mapper;
    }

    private static String handleJsonString(String contentJson) {
        if (contentJson.startsWith("\"{\\\"")) {

            contentJson = contentJson.replace("\\\"", "\"");
            while (contentJson.startsWith("\"")) {
                contentJson = contentJson.substring(1, contentJson.length() - 1);
            }
        }
        return contentJson;
    }

    @Override
    public String serialize(Object o) {
        GspMetadata metadata = (GspMetadata) o;
        MetadataContentSerializer manager = MetadataSerializerHelper.getInstance().getManager(metadata.getHeader().getType());
        if (manager == null) {
            throw new RuntimeException("未能正常获取" + metadata.getHeader().getType() + "的序列化器，请检查配置");
        }
        JsonNode jsonNode = manager.serialize(metadata.getContent());
        JsonNode extRuleNode = null;
        if (metadata.getExtendRule() != null) {
            MdExtendRuleSerializer mdExtRulemanager = MdExtRuleSerializerHelper.getInstance().getManager(metadata.getHeader().getType());
            if (mdExtRulemanager != null) {
                extRuleNode = mdExtRulemanager.serialize(metadata.getExtendRule());
            }
        }

        ObjectMapper objectMapper = Utils.getMapper();
        GspMetadata newMetadata = (GspMetadata) metadata.clone();
        String metadataStr = null;
        try {
            metadataStr = objectMapper.writeValueAsString(newMetadata);
            JsonNode metadataObj = objectMapper.readTree(metadataStr);
            ObjectNode objNode = (ObjectNode) metadataObj;
            objNode.set(MetadataPeopertyUtils.content, jsonNode);
            objNode.set(MetadataPeopertyUtils.extendRule, extRuleNode);
            metadataStr = objectMapper.writeValueAsString(objNode);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return metadataStr;
    }
}
